#include "Python.h"
#include <qnxodbc.h>

#undef Py_BUILD_CORE
#include "datetime.h"

typedef struct {
	UCHAR*		name;
	SDWORD		displen;
	SDWORD		intnlen;
	SWORD		sqltype;
	UDWORD		prec;
	SWORD		scale;
	SWORD		nullable;
	void*		value;
	SDWORD		size;
} COLS;

typedef COLS*	HCOLS;

typedef struct {
	SWORD		sqltype;
	UDWORD		prec;
	SWORD		scale;
	SWORD		nullable;
	void*		value;
	SDWORD		size;
} PARMS;

typedef PARMS*	HPARMS;

RETCODE
SQLAllocCols( HSTMT hstmt, SWORD num_col, HCOLS* phcols )
{
UWORD	col;
HCOLS	hcols;

SWORD	ctype;
SDWORD	size_of;

#define MAX_COLUMN_NAME_LEN 256
UCHAR	name[ MAX_COLUMN_NAME_LEN + 1 ];
SWORD	namelen;

	hcols = (COLS *) calloc( num_col, sizeof( COLS ) );
    for( col = 0; col < num_col; ++col ) {
		if( !SQL_SUCCEEDED( SQLColAttributes( hstmt, (UWORD) (col + 1), SQL_COLUMN_DISPLAY_SIZE, NULL, 0, NULL, &hcols[col].displen ) ) ) {
			free(hcols);
			return SQL_ERROR;
		}
		if( !SQL_SUCCEEDED( SQLColAttributes( hstmt, (UWORD) (col + 1), SQL_COLUMN_LENGTH, NULL, 0, NULL, &hcols[col].intnlen ) ) ) {
			free(hcols);
			return SQL_ERROR;
		}
		if( !SQL_SUCCEEDED( SQLDescribeCol( hstmt, (UWORD) (col + 1), name, sizeof(name), &namelen, &hcols[col].sqltype, &hcols[col].prec, &hcols[col].scale, &hcols[col].nullable) ) ) {
			free(hcols);
			return SQL_ERROR;
		}
		hcols[col].name = (UCHAR *) malloc( namelen + 1 );
		strcpy( hcols[col].name, name );
	}

    for( col = 0; col < num_col; ++col ) {
		switch (hcols[col].sqltype) {
		case SQL_DATE:
			ctype = SQL_C_DATE;
			size_of = sizeof(DATE_STRUCT);
			break;
		case SQL_TIME:
			ctype = SQL_C_TIME;
			size_of = sizeof(TIME_STRUCT);
			break;
		case SQL_TIMESTAMP:
			ctype = SQL_C_TIMESTAMP;
			size_of = sizeof(TIMESTAMP_STRUCT);
			break;
		case SQL_DECIMAL:
		case SQL_NUMERIC:
			ctype = SQL_C_CHAR;
			size_of = hcols[col].intnlen + 1;
			break;
		case SQL_CHAR:
		case SQL_VARCHAR:
		case SQL_LONGVARCHAR:
			ctype = SQL_C_CHAR;
			size_of = hcols[col].intnlen + 1;
			break;
		case SQL_REAL:
		case SQL_FLOAT:
		case SQL_DOUBLE:
			ctype = SQL_C_DOUBLE;
			size_of = sizeof(SDOUBLE);
			break;
		case SQL_TINYINT:
		case SQL_SMALLINT:
		case SQL_INTEGER:
			ctype = SQL_C_LONG;
			size_of = sizeof(SDWORD);
			break;
		case SQL_BIT:
			ctype = SQL_C_BIT;
			size_of = sizeof(UCHAR);
			break;
	    case SQL_BINARY:
	    case SQL_VARBINARY:
	    case SQL_LONGVARBINARY:
			ctype = SQL_C_BINARY;
			size_of = hcols[col].intnlen + 1;
			break;
		default:
			ctype = hcols[col].sqltype;
			size_of = hcols[col].intnlen;
			break;
		}
		hcols[col].value = malloc( size_of );
		SQLBindCol( hstmt, (UWORD) (col + 1), ctype, hcols[col].value, size_of, &hcols[col].size );
	}
	*phcols = hcols;
	return SQL_SUCCESS;
};

RETCODE
SQLFreeCols( HSTMT hstmt, SWORD num_col, HCOLS hcols )
{
UWORD	col;

    for( col = 0; col < num_col; ++col ) {
		free( hcols[col].name );
		free( hcols[col].value );
	}
	free( hcols );

	return SQL_SUCCESS;
}

PyObject *
PyTuple_FromRow( HCOLS hcols, SWORD num_col )
{
UWORD		col;
PyObject*	item;
PyObject*	tuple;

	tuple = PyTuple_New(num_col);
	for( col = 0; col < num_col; ++col ) {
		if( hcols[col].size == SQL_NULL_DATA ) {
			Py_INCREF(Py_None);
			PyTuple_SET_ITEM(tuple, col, Py_None);
		} else {
			switch (hcols[col].sqltype) {
			case SQL_DATE:
				item = PyDate_FromDate( \
					((DATE_STRUCT *)hcols[col].value)->year, \
					((DATE_STRUCT *)hcols[col].value)->month, \
					((DATE_STRUCT *)hcols[col].value)->day);
				break;
			case SQL_TIME:
				item = PyTime_FromTime( \
					((TIME_STRUCT *)hcols[col].value)->hour, \
					((TIME_STRUCT *)hcols[col].value)->minute, \
					((TIME_STRUCT *)hcols[col].value)->second, 0);
				break;
			case SQL_TIMESTAMP:
				item = PyDateTime_FromDateAndTime( \
					((TIMESTAMP_STRUCT *)hcols[col].value)->year, \
					((TIMESTAMP_STRUCT *)hcols[col].value)->month, \
					((TIMESTAMP_STRUCT *)hcols[col].value)->day, \
					((TIMESTAMP_STRUCT *)hcols[col].value)->hour, \
					((TIMESTAMP_STRUCT *)hcols[col].value)->minute, \
					((TIMESTAMP_STRUCT *)hcols[col].value)->second, \
					((TIMESTAMP_STRUCT *)hcols[col].value)->fraction/1000);
				break;
			case SQL_DECIMAL:
			case SQL_NUMERIC:
				item = PyString_FromString((UCHAR *)hcols[col].value);
				item1 = PyEval_ObjectCall();
				break;
			case SQL_CHAR:
			case SQL_VARCHAR:
		    case SQL_LONGVARCHAR:
				item = PyString_FromString((UCHAR *)hcols[col].value);
				break;
			case SQL_REAL:
			case SQL_FLOAT:
			case SQL_DOUBLE:
				item = PyLong_FromDouble(*(SDOUBLE *)hcols[col].value);
				break;
			case SQL_TINYINT:
			case SQL_SMALLINT:
			case SQL_INTEGER:
				item = PyInt_FromLong(*(SDWORD *)hcols[col].value);
				break;
			case SQL_BIT:
				item = PyBool_FromLong(*(UCHAR *)hcols[col].value);
				break;
		    case SQL_BINARY:
		    case SQL_VARBINARY:
		    case SQL_LONGVARBINARY:
				item = &PyBuffer_FromMemory(*(UCHAR *)hcols[col].value, 256*sizeof(UCHAR));
				break;
			}
			PyTuple_SET_ITEM(tuple, col, item);
		}
	}
return tuple;
}

/***************** Imported Object *******************/

PyObject *Decimal;
PyTypeObject *Decimal_Type;


/****************** Error Objects ********************/

#define SQL_SUCCEEDED(rc) (((rc)&(~1))==0)
#define SQL_EXEC(x) \
if( !SQL_SUCCEEDED(x) ) { \
	SQLError( self->henv, self->hdbc, self->hstmt, sqlstate, NULL, error_msg, sizeof( error_msg ), NULL ); \
	PyErr_SetString( Error, error_msg ); \
	return NULL; \
}

UCHAR	sqlstate[ SQLSTATE_SIZE + 1 ];
UCHAR	error_msg[ SQL_MAX_MESSAGE_LENGTH + 1 ];

PyObject* Error;

/****************** Cursor Object ********************/


typedef struct {
	PyObject_HEAD;
	HENV	henv;
	HDBC	hdbc;
	HSTMT	hstmt;
	HCOLS	hcols;
	SWORD	num_col;
	HPARMS	hparms;
} cursor;

static PyTypeObject cursor_type;

static PyObject *
cursor_description(cursor *self, PyObject *args)
{
UWORD	col;

PyObject*	tuple;
PyObject*	item;
PyObject*	pytype;
PyObject*	pynullable;

	tuple = PyTuple_New(self->num_col);
    for( col = 0; col < self->num_col; ++col ) {
	    switch (self->hcols[col].sqltype) {
		case SQL_DATE:
			pytype = (PyObject *)PyDateTimeAPI->DateType;
			break;
		case SQL_TIME:
	        pytype = (PyObject *)PyDateTimeAPI->TimeType;
			break;
		case SQL_TIMESTAMP:
			pytype = (PyObject *)PyDateTimeAPI->DateTimeType;
			break;
		case SQL_DECIMAL:
		case SQL_NUMERIC:
			pytype = (PyObject *)Decimal;
	      	break;
	    case SQL_CHAR:
	    case SQL_VARCHAR:
		case SQL_LONGVARCHAR:
    	    pytype = (PyObject *)&PyString_Type;
			break;
	    case SQL_REAL:
	    case SQL_FLOAT:
	    case SQL_DOUBLE:
			pytype = (PyObject *)&PyFloat_Type;
	        break;
	    case SQL_SMALLINT:
	    case SQL_INTEGER:
	    case SQL_TINYINT:
    	    pytype = (PyObject *)&PyInt_Type;
	        break;
	    case SQL_BIGINT:
	        pytype = (PyObject *)&PyLong_Type;
	        break;
	    case SQL_BIT:
	        pytype = (PyObject *)&PyBool_Type;
	        break;
	    case SQL_BINARY:
	    case SQL_VARBINARY:
	    case SQL_LONGVARBINARY:
    	    pytype = (PyObject *)&PyBuffer_Type;
	        break;
		}

		switch (self->hcols[col].nullable) {
		case SQL_NO_NULLS:
			pynullable = Py_False;
			break;
		case SQL_NULLABLE:
			pynullable = Py_True;
			break;
		case SQL_NULLABLE_UNKNOWN:
			pynullable = Py_None;
			break;
		}
    	item = Py_BuildValue("(sOiiiiO)", self->hcols[col].name, pytype, self->hcols[col].displen, self->hcols[col].intnlen, self->hcols[col].prec, self->hcols[col].scale, pynullable);
		PyTuple_SET_ITEM(tuple, col, item);
		Py_DECREF(pynullable);
    }
	return tuple;
}

static PyObject *
cursor_rowcount(cursor *self, PyObject *args)
{
SDWORD	count;

	SQL_EXEC( SQLRowCount( self->hstmt, &count ) )
	return Py_BuildValue("i", count);
}

static PyObject *
cursor_callproc(cursor *self, PyObject *args, PyObject *params)
{
UCHAR	*SqlStr;

	if (!PyArg_ParseTuple(args, "s", &SqlStr)) {
		return NULL;
	}

	SQL_EXEC( SQLExecDirect( self->hstmt, SqlStr, SQL_NTS ) )

	Py_RETURN_NONE;
}

static PyObject *
cursor_close(cursor *self, PyObject *args)
{
	SQL_EXEC( SQLFreeCols( self->hstmt, self->num_col, self->hcols ) )
	self->num_col = 0;
	SQL_EXEC( SQLFreeStmt( self->hstmt, SQL_CLOSE ) )
	Py_RETURN_NONE;
};

static PyObject *
cursor_execute(cursor *self, PyObject *args)
{
UCHAR	*SqlStr;

	if (!PyArg_ParseTuple(args, "s", &SqlStr)) {
		return NULL;
	}
	SQL_EXEC( SQLExecDirect( self->hstmt, SqlStr, SQL_NTS ) )
	SQL_EXEC( SQLNumResultCols( self->hstmt, &self->num_col ) )
	SQL_EXEC( SQLAllocCols( self->hstmt, self->num_col, &self->hcols ) )
	Py_RETURN_NONE;
};

static PyObject *
cursor_executemany(cursor *self, PyObject *args)
{
UCHAR	*SqlStr;
UWORD	par;
SWORD	num_par;

SWORD	ctype;
SWORD	sqltype;
UDWORD	prec;
SWORD	scale;
SDWORD	nullable;
void**	value;
SDWORD	size;

SDWORD	size_of;

PyObject*	tuple;
PyObject*	item;

	if (!PyArg_ParseTuple(args, "sO", &SqlStr, &tuple)) {
		return NULL;
	}

	SQL_EXEC( SQLPrepare( self->hstmt, SqlStr, SQL_NTS ) )
	SQL_EXEC( SQLNumParams( self->hstmt, &num_par ) )

	if (num_par == PySequence_Size(tuple)) {
		value = calloc( num_par, sizeof( void * ) );
	    for( par = 0; par < num_par; ++par ) {
			item = PySequence_GetItem(tuple, par);
			if( item == Py_None ) {
				size = SQL_NULL_DATA;
				SQL_EXEC( SQLBindParameter( self->hstmt, (par + 1), \
				SQL_PARAM_INPUT, SQL_C_BIT, SQL_VARCHAR, \
				0, 0, NULL, 0, &size) )
			}
			if( item == Py_True || item == Py_False ) {
				value[par] = malloc( sizeof(unsigned char) );
				*((unsigned char *)value[par]) = ( item == Py_True ? 1 : 0 );
				SQL_EXEC( SQLBindParameter( self->hstmt, (par + 1), \
				SQL_PARAM_INPUT, SQL_C_BIT, SQL_BIT, \
				0, 0, value[par], sizeof(unsigned char), NULL) )
			}
			if( PyDateTime_Check(item) ) {
				value[par] = malloc( sizeof(TIMESTAMP_STRUCT) );
				((TIMESTAMP_STRUCT *)value[par])->year     = (SWORD)PyDateTime_GET_YEAR(item);
				((TIMESTAMP_STRUCT *)value[par])->month    = (UWORD)PyDateTime_GET_MONTH(item);
				((TIMESTAMP_STRUCT *)value[par])->day      = (UWORD)PyDateTime_GET_DAY(item);
				((TIMESTAMP_STRUCT *)value[par])->hour     = (UWORD)PyDateTime_DATE_GET_HOUR(item);
				((TIMESTAMP_STRUCT *)value[par])->minute   = (UWORD)PyDateTime_DATE_GET_MINUTE(item);
				((TIMESTAMP_STRUCT *)value[par])->second   = (UWORD)PyDateTime_DATE_GET_SECOND(item);
				((TIMESTAMP_STRUCT *)value[par])->fraction = (UDWORD)(PyDateTime_DATE_GET_MICROSECOND(item) * 1000);
				SQL_EXEC( SQLBindParameter( self->hstmt, (par + 1), \
				SQL_PARAM_INPUT, SQL_C_TIMESTAMP, SQL_TIMESTAMP, \
				0, 0, value[par], sizeof(TIMESTAMP_STRUCT), NULL) )
			}
			if( PyDate_Check(item) ) {
				value[par] = malloc( sizeof(DATE_STRUCT) );
				((DATE_STRUCT *)value[par])->year     = (SWORD)PyDateTime_GET_YEAR(item);
				((DATE_STRUCT *)value[par])->month    = (UWORD)PyDateTime_GET_MONTH(item);
				((DATE_STRUCT *)value[par])->day      = (UWORD)PyDateTime_GET_DAY(item);
				SQL_EXEC( SQLBindParameter( self->hstmt, (par + 1), \
				SQL_PARAM_INPUT, SQL_C_DATE, SQL_DATE, \
				0, 0, value[par], sizeof(DATE_STRUCT), NULL) )
			}
			if( PyTime_Check(item) ) {
				value[par] = malloc( sizeof(TIME_STRUCT) );
				((TIME_STRUCT *)value[par])->hour     = (UWORD)PyDateTime_DATE_GET_HOUR(item);
				((TIME_STRUCT *)value[par])->minute   = (UWORD)PyDateTime_DATE_GET_MINUTE(item);
				((TIME_STRUCT *)value[par])->second   = (UWORD)PyDateTime_DATE_GET_SECOND(item);
				SQL_EXEC( SQLBindParameter( self->hstmt, (par + 1), \
				SQL_PARAM_INPUT, SQL_C_TIME, SQL_TIME, \
				0, 0, value[par], sizeof(TIME_STRUCT), NULL) )
			}
			if( PyInt_Check(item) ) {
				value[par] = malloc( sizeof(long) );
				*((long *)value[par]) = PyInt_AsLong(item);
				SQL_EXEC( SQLBindParameter( self->hstmt, (par + 1), \
				SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, \
				0, 0, value[par], sizeof(long), NULL) )

			}
			if( PyLong_Check(item) ) {
				value[par] = malloc( sizeof(long) );
				*((long *)value[par]) = PyLong_AsLong(item);
				SQL_EXEC( SQLBindParameter( self->hstmt, (par + 1), \
				SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, \
				0, 0, value[par], sizeof(long), NULL) )
			}
			if( PyFloat_Check(item) ) {
				value[par] = malloc( sizeof(double) );
				*((long *)value[par]) = PyFloat_AsDouble(item);
				SQL_EXEC( SQLBindParameter( self->hstmt, (par + 1), \
				SQL_PARAM_INPUT, SQL_C_DOUBLE, SQL_DOUBLE, \
				0, 0, value[par], sizeof(double), NULL) )
			}
			if( PyString_Check(item) || PyUnicode_Check(item) ) {
				prec = PyString_Size(item);
				value[par] = PyString_AsString(item);
				SQL_EXEC( SQLBindParameter( self->hstmt, (par + 1), \
				SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, \
				0, 0, value[par], prec, NULL) )
			}
	    }
	} else {
		PyErr_SetString( Error, "Parametrs count don't match" );
		return NULL;
	}

	SQL_EXEC( SQLExecute( self->hstmt ) )
	SQL_EXEC( SQLNumResultCols( self->hstmt, &self->num_col ) )
	SQL_EXEC( SQLAllocCols( self->hstmt, self->num_col, &self->hcols ) )
	Py_RETURN_NONE;
};

static PyObject *
cursor_fetchone(cursor *self, PyObject *args)
{
	switch ( SQLFetch( self->hstmt ) ) {
	case SQL_SUCCESS:
	case SQL_SUCCESS_WITH_INFO:
		return PyTuple_FromRow( self->hcols, self->num_col );
	case SQL_NO_DATA_FOUND:
		Py_RETURN_NONE;
	default:
		SQLError( self->henv, self->hdbc, self->hstmt, sqlstate, NULL, error_msg, sizeof( error_msg ), NULL );
		PyErr_SetString( Error, error_msg );
		return NULL;
	}
};

static PyObject *
cursor_fetchmany(cursor *self, PyObject *args)
{
UDWORD		num_row = 1;
UDWORD		row;

PyObject*	list;
PyObject*	tuple;

	if (!PyArg_ParseTuple(args, "|l", &num_row)) {
		return NULL;
	}

	list = PyList_New(0);
	for( row = 0; row < num_row; ++row) {
		switch ( SQLFetch( self->hstmt ) ) {
		case SQL_SUCCESS:
		case SQL_SUCCESS_WITH_INFO:
			tuple = PyTuple_FromRow( self->hcols, self->num_col );
			PyList_Append(list, tuple);
		case SQL_NO_DATA_FOUND:
			break;
		default:
			SQLError( self->henv, self->hdbc, self->hstmt, sqlstate, NULL, error_msg, sizeof( error_msg ), NULL );
			PyErr_SetString( Error, error_msg );
			return NULL;
		}
	}
return list;
};

static PyObject *
cursor_fetchall(cursor *self, PyObject *args)
{
	Py_RETURN_NONE;
};

static PyObject *
cursor_next(cursor *self, PyObject *args)
{
	switch ( SQLFetch( self->hstmt ) ) {
	case SQL_SUCCESS:
	case SQL_SUCCESS_WITH_INFO:
		return PyTuple_FromRow( self->hcols, self->num_col );
	case SQL_NO_DATA_FOUND:
		return NULL;
	default:
		SQLError( self->henv, self->hdbc, self->hstmt, sqlstate, NULL, error_msg, sizeof( error_msg ), NULL );
		PyErr_SetString( Error, error_msg );
		return NULL;
	}
};

static PyObject *
cursor_scroll(cursor *self, PyObject *args)
{
	Py_RETURN_NONE;
};

static PyObject *
cursor_getiter(cursor *self)
{
	Py_INCREF(self);
	return (PyObject *)self;
}

static PyMethodDef cursor_methods[] = {
	{"description", (PyCFunction)cursor_description, METH_VARARGS,
	 	PyDoc_STR("description")},
	{"rowcount", (PyCFunction)cursor_rowcount, METH_VARARGS,
	 	PyDoc_STR("rowcount")},
	{"callproc", (PyCFunction)cursor_callproc, METH_VARARGS,
	 	PyDoc_STR("callproc")},
	{"close", (PyCFunction)cursor_close, METH_NOARGS,
	 	PyDoc_STR("close")},
	{"execute", (PyCFunction)cursor_execute, METH_VARARGS,
	 	PyDoc_STR("execute")},
	{"executemany", (PyCFunction)cursor_executemany, METH_VARARGS,
	 	PyDoc_STR("executemany")},
	{"fetchone", (PyCFunction)cursor_fetchone, METH_NOARGS,
	 	PyDoc_STR("fetchone")},
	{"fetchmany", (PyCFunction)cursor_fetchmany, METH_VARARGS,
	 	PyDoc_STR("fetchmany")},
	{"fetchall", (PyCFunction)cursor_fetchall, METH_VARARGS,
	 	PyDoc_STR("fetchall")},
	{NULL},
};

static int
cursor_init(cursor *self, PyObject *args)
{

    if( SQLAllocStmt( self->hdbc, &self->hstmt ) != SQL_SUCCESS ) {
		return -1;
	}

	if( !SQL_SUCCEEDED( SQLSetScrollOptions( self->hstmt, SQL_CONCUR_VALUES, SQL_SCROLL_DYNAMIC, 1 ) ) ) {
		SQLFreeStmt( self->hstmt, SQL_DROP );
		return -1;
    } 

    return 0;
}

//cursor_nextset()
//arraysize
//cursor_setinputsizes()
//cursor_setoutputsizes()

static PyTypeObject cursor_type = {
	PyObject_HEAD_INIT(NULL)
	0,
	"sybase.cursor",
	sizeof(cursor),
	0,
	0,					/* tp_dealloc */
	0,					/* tp_print */
	0,					/* tp_getattr */
	0,					/* tp_setattr */
	0,					/* tp_compare */
	0,					/* tp_repr */
	0,					/* tp_as_number */
	0,					/* tp_as_sequence */
	0,					/* tp_as_mapping */
	0,					/* tp_hash */
	0,					/* tp_call */
	0,					/* tp_str */
	0,					/* tp_getattro */
	0,					/* tp_setattro */
	0,					/* tp_as_buffer */
	Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
	0,					/* tp_doc */
	0,					/* tp_traverse */
	0,					/* tp_clear */
	0,					/* tp_richcompare */
	0,					/* tp_weaklistoffset */
	(getiterfunc)cursor_getiter,		/* tp_iter */
	(iternextfunc)cursor_next,		/* tp_iternext */
	cursor_methods,		/* tp_methods */
	0,					/* tp_members */
	0,					/* tp_getset */
	0,					/* tp_base */
	0,					/* tp_dict */
	0,					/* tp_descr_get */
	0,					/* tp_descr_set */
	0,					/* tp_dictoffset */
	(initproc)cursor_init,		/* tp_init */
	PyType_GenericAlloc,	/* tp_alloc */
	PyType_GenericNew,	/* tp_new */
	PyObject_Del,		/* tp_free */
};

/******************* Connection Object ***************/

typedef struct {
	PyObject_HEAD;
	HENV henv;
	HDBC hdbc;
} connection;

static PyTypeObject connection_type;

static PyObject *
connection_close(connection *self, PyObject *args)
{
	SQLTransact( self->henv, self->hdbc, SQL_ROLLBACK );
	SQLDisconnect( self->hdbc );
	SQLFreeConnect( self->hdbc );
	SQLFreeEnv( self->henv );
	Py_RETURN_NONE;
}

static PyObject *
connection_commit(connection *self, PyObject *args)
{
	SQLTransact(self->henv, self->hdbc, SQL_COMMIT);
	Py_RETURN_NONE;
}

static PyObject *
connection_rollback(connection *self, PyObject *args)
{
	SQLTransact(self->henv, self->hdbc, SQL_ROLLBACK);
	Py_RETURN_NONE;
}

static cursor *
connection_cursor(connection *self, PyObject *args)
{
HSTMT	hstmt;
cursor*	cur;

	cur = PyObject_New(cursor, &cursor_type);
	cur->henv = self->henv;
	cur->hdbc = self->hdbc;
    cur->ob_type->tp_init((PyObject *)cur, NULL, NULL);

	return cur;
}

static PyMethodDef connection_methods[] = {
	{"close", (PyCFunction)connection_close, METH_NOARGS,
	 	PyDoc_STR("close connection")},
	{"commit", (PyCFunction)connection_commit, METH_NOARGS,
	 	PyDoc_STR("commit work")},
	{"rollback", (PyCFunction)connection_rollback, METH_NOARGS,
	 	PyDoc_STR("rollback work")},
	{"cursor", (PyCFunction)connection_cursor, METH_VARARGS,
	 	PyDoc_STR("new cursor")},
	{NULL},
};

static int
connection_init(connection *self, PyObject *args)
{
UCHAR	*DSN; 
UCHAR	*UID;
UCHAR	*AuthStr;

unsigned short	mcnl;

	if (!PyArg_ParseTuple(args, "sss", &DSN, &UID, &AuthStr)) {
		return -1;
	}
    if( SQLAllocEnv( &self->henv ) != SQL_SUCCESS ) {
		return -1;
	}
    if( SQLAllocConnect( self->henv, &self->hdbc ) != SQL_SUCCESS ) {
        SQLFreeEnv( self->henv );
        return -1;
    }
	if( SQLConnect( self->hdbc, DSN, SQL_NTS, UID, SQL_NTS, AuthStr, SQL_NTS ) != SQL_SUCCESS ) {
		SQLError( self->henv, self->hdbc, NULL, sqlstate, NULL, error_msg, sizeof( error_msg ), NULL );
		PyErr_SetString( Error, error_msg );
		SQLFreeConnect( self->hdbc );
        SQLFreeEnv( self->henv );
		return -1;
	}

	SQLGetInfo(self->hdbc, SQL_MAX_COLUMN_NAME_LEN, &mcnl, NULL, NULL);
	return 0;
}

static void
connection_dealloc(connection* self)
{
    if (self->hdbc != SQL_NULL_HDBC)
    {
		SQLTransact( &self->henv, &self->hdbc, SQL_ROLLBACK );
		SQLDisconnect( &self->hdbc );
		SQLFreeConnect( &self->hdbc );
		SQLFreeEnv( &self->henv );
    }

    self->ob_type->tp_free((PyObject *)self);
}

static PyTypeObject connection_type = {
	PyObject_HEAD_INIT(NULL)
	0,
	"sybase.connection",
	sizeof(connection),
	0,
	(destructor)connection_dealloc,	/* tp_dealloc */
	0,					/* tp_print */
	0,					/* tp_getattr */
	0,					/* tp_setattr */
	0,					/* tp_compare */
	0,					/* tp_repr */
	0,					/* tp_as_number */
	0,					/* tp_as_sequence */
	0,					/* tp_as_mapping */
	0,					/* tp_hash */
	0,					/* tp_call */
	0,					/* tp_str */
	0,					/* tp_getattro */
	0,					/* tp_setattro */
	0,					/* tp_as_buffer */
	Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
	0,					/* tp_doc */
	0,					/* tp_traverse */
	0,					/* tp_clear */
	0,					/* tp_richcompare */
	0,					/* tp_weaklistoffset */
	0,					/* tp_iter */
	0,					/* tp_iternext */
	connection_methods,			/* tp_methods */
	0,					/* tp_members */
	0,					/* tp_getset */
	0,					/* tp_base */
	0,					/* tp_dict */
	0,					/* tp_descr_get */
	0,					/* tp_descr_set */
	0,					/* tp_dictoffset */
	(initproc)connection_init,	/* tp_init */
	PyType_GenericAlloc,	/* tp_alloc */
	PyType_GenericNew,	/* tp_new */
	PyObject_Del,		/* tp_free */
};

/* Module */

static char sybase_doc[] =
    "A DB API 2.0 module for Sybase 5.5.05 database.";

static PyMethodDef sybase_methods[] =
{
	{ NULL, NULL, 0, NULL}
};

PyMODINIT_FUNC
initsybase(void)
{
	PyObject *m;
	PyObject *d;

	/* Fill in deferred data addresses.  This must be done before
	   PyType_Ready() is called.  Note that PyType_Ready() automatically
	   initializes the ob.ob_type field to &PyType_Type if it's NULL,
	   so it's not necessary to fill in ob_type first. */

	m = Py_InitModule3("sybase", sybase_methods, sybase_doc);
	if (m == NULL)
		return;

    PyModule_AddIntConstant(m, "threadsafety", 1);
    PyModule_AddStringConstant(m, "apilevel", "2.0");
    PyModule_AddStringConstant(m, "paramstyle", "qmark");

	PyDateTimeAPI = (PyDateTime_CAPI *)PyCObject_Import("datetime", "datetime_CAPI");
	if (!PyDateTimeAPI) {
		PyErr_SetString(PyExc_RuntimeError, "Unable to import datetime module");
		return;
	}

    d = PyImport_ImportModule("decimal");
    if (d == NULL)
    {
        PyErr_SetString(PyExc_RuntimeError, "Unable to import decimal module");
        return;
    }

    Decimal = PyObject_GetAttrString(d, "Decimal");
    if (Decimal == NULL)
        PyErr_SetString(PyExc_RuntimeError, "Unable to import decimal.Decimal");

	Decimal_Type = (PyTypeObject *)PyObject_Type(Decimal);
    Py_DECREF(d);

    if (Decimal_Type == NULL)
        PyErr_SetString(PyExc_RuntimeError, "Unable to import <type decimal.Decimal>");

	Error = PyErr_NewException("sybase.error", NULL, NULL);
	Py_INCREF(Error);

	PyModule_AddObject(m, "sqlerror", Error);	

	if (PyType_Ready(&connection_type) < 0)
		return;

	Py_INCREF(&connection_type);
	PyModule_AddObject(m, "connection", (PyObject *) &connection_type);

	if (PyType_Ready(&cursor_type) < 0)
		return;

	Py_INCREF(&cursor_type);
	PyModule_AddObject(m, "cursor", (PyObject *) &cursor_type);

}
